<template>
  <view class="mxp-textarea" :class="[cusClass]" :style="[textareaViewStyle, cusStyle]">
    <textarea
      class="textarea"
      :value="data.innerValue"
      :style="[{ height: autoHeight ? 'auto' : height, textAlign }, textareaStyle]"
      :placeholder="newPlaceholder"
      :placeholderStyle="newPlaceholderStyle"
      :placeholderClass="placeholderClass"
      :disabled="disabled"
      :focus="focus"
      :autoHeight="autoHeight"
      :fixed="fixed"
      :cursorSpacing="cursorSpacing"
      :cursor="cursor"
      :showConfirmBar="showConfirmBar"
      :selectionStart="selectionStart"
      :selectionEnd="selectionEnd"
      :adjustPosition="adjustPosition"
      :disableDefaultPadding="disableDefaultPadding"
      :holdKeyboard="holdKeyboard"
      :maxlength="maxlength"
      :confirmType="confirmType"
      :ignoreCompositionEvent="ignoreCompositionEvent"
      :confirmHold="confirmHold"
      @focus="onFocus"
      @blur="onBlur"
      @linechange="onLinechange"
      @input="onInput"
      @confirm="onConfirm"
      @keyboardheightchange="onKeyboardheightchange"
    ></textarea>
    <view class="count-view">
      <template v-if="count">
        <text class="current-numb">{{ data.innerValue.length }}</text>
        <text class="partition">/</text>
        <text class="max-numb">{{ maxlength }}</text>
      </template>
      <text v-if="clearable && !disabled" class="clear" :style="[cancelStyle]" @click.stop.prevent="cancelValueClick">清除</text>
    </view>
  </view>
</template>

<script lang="ts" setup>
  import { publicStyeProps } from '../mxp-tool/utils/public-props';
  import { cssObj2Str } from '../mxp-tool/utils/dom';
  import { type CSSProperties, type PropType, computed, inject, nextTick, onMounted, shallowReactive, watch } from 'vue';
  import type { $AllColorStr, $SizeUnitStr } from '@/mxp-ui/mxp-tool/types/interface';
  import { type FormID, formInjectKey, formItemPropInjectKey, mxpFormStore } from '../mxp-tool/store/mxp-store-form';
  import type {
    TextareaConfirmType,
    TextareaOnBlurEvent,
    TextareaOnConfirmEvent,
    TextareaOnFocusEvent,
    TextareaOnInputEvent,
    TextareaOnKeyboardheightchangeEvent,
    TextareaOnLinechangeEvent,
  } from 'mxp-uni-types';

  defineOptions({
    name: 'mxp-textarea',
  });

  const props = defineProps({
    /** 绑定输入框的内容 */
    modelValue: { type: [String, Number], default: '' },
    /** 对齐方式 */
    textAlign: { type: String as PropType<'center' | 'left' | 'right'>, default: 'left' },
    /** 对齐方式 */
    background: { type: String as PropType<$AllColorStr>, default: '#ffffff' },
    /** 边框类型，surround-四周边框，bottom-底部边框 为 false 则不显示边框 */
    borderColor: { type: [Boolean, String] as PropType<$AllColorStr | boolean>, default: true },
    /** 圆角 */
    borderRadius: { type: String as PropType<$SizeUnitStr>, default: '4px' },
    /** 是否显示清除按钮图标 */
    clearable: { type: Boolean, default: true },
    /** 没有内容时的占位提示文字 */
    placeholder: { type: [String, Number], default: '' },
    /** 指定placeholder的样式类，注意页面或组件的style中写了scoped时，需要在类名前写/deep/部分平台可能还需要写到 app.vue中才生效 */
    placeholderClass: { type: String, default: 'textarea-placeholder' },
    /** 指定placeholder的样式 */
    placeholderStyle: { type: [String, Object] as PropType<CSSProperties | string>, default: `color: #c0c4cc` },
    /** 输入框高度 */
    height: { type: String as PropType<$SizeUnitStr>, default: '80px' },
    /** 设置键盘右下角按钮的文字，仅微信小程序，App-vue和H5有效 */
    confirmType: { type: String as PropType<TextareaConfirmType>, default: 'done' },
    /** 是否禁用 */
    disabled: { type: Boolean, default: false },
    /** 是否显示统计字数 */
    count: { type: Boolean, default: true },
    /** 是否自动获取焦点，nvue不支持，H5取决于浏览器的实现 */
    focus: { type: Boolean, default: false },
    /** 是否自动增加高度 */
    autoHeight: { type: Boolean, default: false },
    /** 如果textarea是在一个position:fixed的区域，需要显示指定属性fixed为true */
    fixed: { type: Boolean, default: false },
    /** 指定光标与键盘的距离 */
    cursorSpacing: { type: Number, default: 0 },
    /** 指定focus时的光标位置 */
    cursor: { type: Number, default: undefined },
    /** 是否显示键盘上方带有”完成“按钮那一栏 */
    showConfirmBar: { type: Boolean, default: true },
    /** 光标起始位置，自动聚焦时有效，需与selection-end搭配使用 */
    selectionStart: { type: Number, default: -1 },
    /** 光标结束位置，自动聚焦时有效，需与selection-start搭配使用 */
    selectionEnd: { type: Number, default: -1 },
    /** 键盘弹起时，是否自动上推页面 */
    adjustPosition: { type: Boolean, default: true },
    /** 是否去掉 iOS 下的默认内边距，只微信小程序有效 */
    disableDefaultPadding: { type: Boolean, default: false },
    /** focus时，点击页面的时候不收起键盘，只微信小程序有效 */
    holdKeyboard: { type: Boolean, default: false },
    /** 最大输入长度，设置为 -1 的时候不限制最大长度 */
    maxlength: { type: Number, default: 140 },
    /** 是否忽略组件内对文本合成系统事件的处理 */
    ignoreCompositionEvent: { type: Boolean, default: true },
    /** 是否忽略组件内对文本合成系统事件的处理 */
    confirmHold: { type: Boolean, default: false },
    /** 文本样式 */
    textareaStyle: { type: Object as PropType<CSSProperties>, default: () => ({}) },
    /** 清除文字的样式 */
    cancelStyle: { type: Object as PropType<CSSProperties>, default: () => ({}) },
    ...publicStyeProps,
  });
  const emits = defineEmits<{
    (e: 'update:modelValue', str: number | string): void;
    (e: 'input', event: TextareaOnInputEvent): void;
    (e: 'blur', event: TextareaOnBlurEvent): void;
    (e: 'focus', event: TextareaOnFocusEvent): void;
    (e: 'linechange', event: TextareaOnLinechangeEvent): void;
    (e: 'keyboardheightchange', event: TextareaOnKeyboardheightchangeEvent): void;
    (e: 'confirm', event: TextareaOnConfirmEvent): void;
    (e: 'change', str: number | string): void;
    (e: 'cancel', str: string): void;
  }>();

  const data = shallowReactive({
    innerValue: String(props.modelValue),
    // 是否处于获得焦点状态
    focused: false,
  });

  watch(
    () => props.modelValue,
    (newVal) => {
      data.innerValue = String(newVal);
    }
  );

  /** 最终的占位字符串 */
  const newPlaceholder = computed(() => String(props.placeholder || ''));

  /** 最终的占位文字样式 */
  const newPlaceholderStyle = computed(() => cssObj2Str(props.placeholderStyle));

  /** 组件的样式 */
  const textareaViewStyle = computed<CSSProperties>(() => {
    const style: CSSProperties = { backgroundColor: props.background, borderRadius: props.borderRadius };
    if (props.borderColor) {
      style.borderColor = props.borderColor === true ? '#d6d7d9' : props.borderColor;
      style.borderStyle = 'dashed';
      style.borderWidth = '1px';
    }
    return style;
  });
  let mxpFromIDInjectKey: FormID | undefined = undefined;
  let propInjectKey: number | string | undefined = undefined;
  onMounted(() => {
    mxpFromIDInjectKey = inject<FormID>(formInjectKey);
    propInjectKey = inject<number | string>(formItemPropInjectKey);
  });

  /** 聚焦事件 */
  function onFocus(event: TextareaOnBlurEvent) {
    emits('focus', event);
  }

  /** 失焦事件 */
  function onBlur(event: TextareaOnBlurEvent) {
    emits('blur', event);
    // 尝试调用mxp-form组件的验证
    const validateField = mxpFromIDInjectKey ? mxpFormStore().state[mxpFromIDInjectKey]?.validateField : undefined;
    typeof validateField === 'function' && (propInjectKey || propInjectKey === 0) && validateField(propInjectKey, 'blur');
  }

  /** 输入框行数变化时调用 抖音小程序、飞书小程序、快手小程序不支持 */
  function onLinechange(event: TextareaOnLinechangeEvent) {
    emits('linechange', event);
  }

  /** 当键盘输入时，触发 input 事件，event.detail = {value, cursor}， @input 处理函数的返回值并不会反映到 textarea 上 */
  function onInput(event: TextareaOnInputEvent) {
    nextTick(() => {
      emits('input', event);
      data.innerValue = event.detail.value;
      valueChange();
    });
  }

  /** 内容发生变化，进行处理 */
  function valueChange() {
    nextTick(() => {
      emits('update:modelValue', data.innerValue);
      emits('change', data.innerValue);
      // 尝试调用mxp-form组件的验证
      const validateField = mxpFromIDInjectKey ? mxpFormStore().state[mxpFromIDInjectKey]?.validateField : undefined;
      typeof validateField === 'function' && (propInjectKey || propInjectKey === 0) && validateField(propInjectKey, 'change');
    });
  }

  /** 点击完成时， 触发 confirm 事件，event.detail = {value: value} */
  function onConfirm(event: TextareaOnConfirmEvent) {
    try {
      uni.hideKeyboard();
    } catch (e) {}
    nextTick(() => {
      emits('confirm', event);
    });
  }

  /** 键盘高度发生变化的时候触发此事件，event.detail = {height: height, duration: duration} */
  function onKeyboardheightchange(event: TextareaOnKeyboardheightchangeEvent) {
    emits('keyboardheightchange', event);
  }

  /**
   * 清除输入
   */
  function cancelValueClick() {
    data.innerValue = '';
    emits('cancel', '');
    valueChange();
  }
</script>

<style lang="scss" scoped>
  .mxp-textarea {
    display: flex;
    flex-direction: column;
    align-items: stretch;
    justify-content: flex-start;

    &.disabled {
      opacity: 0.6;
    }

    .textarea {
      width: auto;
      padding: 10px;
      font-size: 15px;
      color: #333333;
    }

    .count-view {
      display: flex;
      flex-direction: row;
      align-items: center;
      justify-content: flex-end;
      margin-right: 5px;

      .current-numb {
        font-size: 12px;
        color: #999999;
      }

      .partition {
        padding: 0 3px;
        font-size: 12px;
        color: #333333;
      }

      .max-numb {
        font-size: 12px;
        color: #333333;
      }

      .clear {
        display: flex;
        padding: 6px;
        margin-left: 5px;
        font-size: 12px;
        color: #223aec;
      }
    }
  }
</style>
